# coding: UTF-8
#--
# Copyright (C) 2012-2014 Lapis Lazuli Texts
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see <http://www.gnu.org/licenses/>.

module Sanzang

  # A translation table encapsulates a set of rules for translating with the
  # \Sanzang system.
  #
  class TranslationTable

    # The records for the translation table, as an array
    #
    attr_reader :records

    # Original encoding when the table was read
    #
    attr_reader :source_encoding  

    # The translation table file format is summarized as follows:
    #
    # - Each line of text is a record for a translation rule.
    # - Fields in the record are separated by the "|" character.
    # - The first field contains the term in the source language.
    # - Subsequent fields are equivalent terms in destination languages.
    # - The number of columns must be consistent for the entire table.
    #
    # The rules passed in here may either be a file descriptor or a string.
    #
    def initialize(rules)
      contents = rules.kind_of?(String) ? rules : rules.read
      @source_encoding = contents.encoding
      contents.encode!(Encoding::UTF_8)

      if contents =~ /~\||\|~|\| /       # If there is any old formatting...
        contents.gsub!(/~\||\|~/, "")    # Rm old style "~|" and "|~"
        contents.gsub!(/^\s+|\s+$/, "")  # Rm WS around lines
        contents.gsub!(/\s*\|\s*/, "|")  # Rm WS around delimiters
      end

      @records = contents.strip.split("\n").collect {|r| r.strip.split("|") }
      @sorted = false
      check_dims
     #sort!
    end

    # Retrieve a record by its numeric index.
    #
    def [](index)
      @records[index]
    end

    # Check the basic dimensions of the translation table
    #
    def check_dims
      if @records.size < 1
        raise "Table must have at least 1 row"
      elsif records[0].size < 2
        raise "Table must have at least 2 columns"
      end
      @records.each do |r|
        if r.size != width
          raise "Column mismatch: Line #{i + 1}"
        end
      end
    end

    # Check if the table records are sorted
    #
    def sorted?
      @sorted
    end

    # Reverse sort all records by length
    #
    def sort!
      @records.sort! {|x,y| y[0].size <=> x[0].size }
      @sorted = true
      nil
    end

    # The text encoding used internally for all translation table data
    #
    def encoding
      Encoding::UTF_8
    end

    # Find a record by the source language term (first column).
    #
    def find(term)
      @records.find {|rec| rec[0] == term }
    end

    # Convert to a hash. The original records are the values.
    #
    # For example: "A" => ["A", "B", "C"]
    #
    def to_h
      h = Hash.new
      @records.each {|rec| h[rec[0]] = rec if not h[rec[0]] }
      h
    end

    # Only include unique source values. The resulting table is unsorted.
    #
    def uniq!
      @records = to_h.values
      @sorted = false
      nil
    end

    # Merge another table into this one. If the same source term exists in
    # both tables, then the record from the other table will be used instead.
    # Note: after a merge, the resulting table is unsorted.
    #
    def merge!(tab2)
      if tab2.width != width
        raise "Table widths must match when merging tables"
      end
      h1 = to_h
      tab2.records.each do |rec|
        h1[rec[0]] = rec
      end
      @records = h1.values
      @sorted = false
    end

    # Return a CSV formatted string
    #
    def to_csv
      @records.map {|r| r.join("|") }.join("\n")
    end

    # The number of records in the table
    #
    def length
      @records.length
    end

    # The number of columns in the table
    #
    def width
      @records[0].length
    end

  end
end
